

                     L                ZZZZZZ         RRRRR           SSSSS
                     L                    Z          R    R         S
                     L          aaa      Z      aaa  R    R  u   u  S
                     L            a     Z         a  RRRRR   u   u  SSSSS
               XX    L         aaaa    Z       aaaa  R    R  u   u       S
              XXXX   L        a   a   Z       a   a  R    R  u   u       S
             XXXXXX  LLLLLLL  aaaaa  ZZZZZZZ  aaaaa  R    R  uuuuu  SSSSSS
             XXXXXX       
        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
             XXXXXX
             XXXXXX
              XXXX     proudly presents his 30.Cracking Tutorial (13.10.1999)
               XX            My solution of the 2nd Kraecker mAG Project

I.   Introduction
I.1  The tools
II.  The essay
II.1 The CrackMe
II.2 Clonk Planet
III. BTW

I.   Introduction
     I participated (again) at the Cracking project of the German Scene EZine Kraecker. The 
     targets to crack were "projekt A" by cg! and tCH and the game Clonk Planet 4.50 - 
     The better you crack them, the more points you achieve. I have to explain the solutions
     shortly, but as I don't like short solutions, I can write a tut about it.

I.1  The tools
     For the CrackMe:
       SoftIce (I have v3.23)
       MASM (to compile the KeyGen)
     For Clonk Planet:
       SoftIce
       MASM (to compile the KeyGen)
     Intellectual support: The CD "Follow the Blind" from Blind Guardian

II.  The essay
II.1 The CrackMe
     A small look at the exe with an hexeditor reveals that it is packed with Neolite (see
     the section that is called .neolit ?). OK, the first rule was "No patching" anyway.
     So start the CrackMe and enter any name/serial combination. The CrackMe terminates
     and we have to restart it again. This time we set a breakpoint on hmemcpy (bpx hmemcpy)
     before push the "oK!" button. When SICE breaks, we push F12 until we reach 32bit code.
     You recognize 32bit code when the memory address has the following form: XXXX:XXXXXXXX
     When we reached it, we press F10 until you reach code where there's no "ret" in the next
     5 lines. Now we have reached the part where it gets interesting. When we start tracing
     through the code (F10) we soon find a loop where a value gets calculated from our name.
     This is a fake calculation. Trace on until you reach this.

     -- THIS LOOP ADDS EVERY CHAR OF THE NAME AND ADDS 0F FOR EVERY CHAR
     :00431D1F   mov eax, [ebp-08] ;; EAX = NAME
     :00431D22   movxz eax, byte ptr [edi+eax-01] ;; EAX = i. CHAR
     :00431D27   add [ebp-0C], eax ;; ADD EAX TO DWORD PTR [EBP-0C]
     :00431D2A   mov ebx, [ebp-0C] ;; LOAD DWORD PTR [EBP-0C] IN EBX
     :00431D2D   add ebx, 0F
     :00431D33   inc edi
     :00431D34   dec esi
     :00431D35   jne 431D1F
     -- END OF FIRST LOOP
     :00431D37   lea edx, [ebp-20]
     :00431D3A   mov eax, [ebp-04]
     :00431D1D   mov eax, [eax+01E4]
     :00431D43   call 41ACB0
     :00431D48   mov eax, [ebp-20] ;; EAX POINTS TO SERIAL
     :00431D4B   call 406794 ;; HERE THE SERIAL IS LOADED INTO EAX
     :00431D50   mov esi, eax ;; MOVE SERIAL INTO ESI
     :00431D52   sub esi, 05C4E51F ;; SUBTRACT 05C4E51F FROM SERIAL
     :00431D58   xor esi, 09 ;; XOR SERIAL WITH 9
     :00431D5B   mov eax, [ebp-08] ;; EAX POINTS TO NAME
     :00431D5E   call 40390C ;; THIS CALL GETS LENGTH OF NAME
     :00431D63   sub esi, eax ;; SUBTRACTS LENGTH FROM SERIAL
     :00431D65   cmp ebx, esi ;; COMPARES VALUE FROM FIRST LOOP WITH SERIAL
     :00431D67   jne 431DDF ;; IF NOT SAME, THEN JUMP

     So - after we have the value from the first loop - we have to solve the following
     equation:

     Serial = ((LoopValue + LengthOfName) XOR 9) + 05C4E51F

     You will find the KeyGen algo (MASM and C) in the zipfile.
     A process patcher which will patch byte 431D68 to 00 is included, too.
     Addition to the keygen: Actually the name must be bigger than 5 chars to be accepted by the
     CrackMe. I didn't include this in my keygen, as if you have *really* quick eyes (or you
     slow down your machine ;) you can see that the "Cracked" message will appear before it
     closes, even if you have the correct serial for a 1-char-name. 

II.2 Clonk Planet
     This program is packed, too. But this time we have to deal with ASPack and not with
     Neolite. But that doesn't really matter, as we will keygen it. Start Clonk Planet
     (btw: a *really* sucking game ;) and enter a name/serial. Then we use GetWindowTextA
     as breakpoint and hit the OK button. I will choose LaZaRuS as name and 666999 as 
     serial. I would suggest to take the same values to understand the essay. When SICE 
     breaks hit F12 and you will be back in 32bit code. Now look at eax. Does it have the
     value 2 or 7? If it has 2, the button-text (OK) has been read, if it has 7 the
     name (LaZaRuS) has been read. If the buttontext has been read press CTRL-D. SICE 
     will break again. F12 again and set a breakpoint on the call GetWindowTextA (45DF22)
     Now hit CTRL-D again and SICE will break, when the serial is read. Disable the
     "bpx GetWindowTextA". If you consult your API reference you will see, that eax is
     used as pointer to the buffer where the name and serial should be stored. Get out of
     SICE and re-enter your name/serial. Hit "OK" again and it will break at 45DF22. Now
     enter "d eax" and hit F10. You will see your name stored in memory (if name is read).
     Now set a bpm on the first char of your name. In my case it is "bpm B6633C rw" - the
     memory address changes every time you try to register. Now disable all bpx except
     the bpm with "bd" and hit CTRL-D. 

     The first two times SICE breaks on memory access are useless for calculation and
     you can easily find out what's checked on your own. The first important operation is
     at instruction 436659. You will see this:

     :00000000 8A16                    mov dl, byte ptr [esi] ;; DL = 1st CHAR OF NAME
     :00000002 84D2                    test dl, dl ;; IS DL == 0?
     :00000004 7415                    je 0000001B ;; IF SO, JUMP
     :00000006 57                      push edi
     :00000007 0FBED2                  movsx edx, dl ;; EDX = DL
     :0000000A 8BF9                    mov edi, ecx
     :0000000C 0FAFFA                  imul edi, edx ;; EDI = EDI * EDX
     :0000000F 8A543101                mov dl, byte ptr [ecx+esi+01] ;; DL = NEXT CHAR
     :00000013 03C7                    add eax, edi ;; EAX SAVES THE RESULT
     :00000015 41                      inc ecx ;; NEXT CHAR
     :00000016 84D2                    test dl, dl ;; IF DL = 0 (NO MORE CHAR)
     :00000018 75ED                    jne 00000007 ;; IF STILL CHARS LEFT, THEN JUMP

     After this loop we have a "magic" value in eax which is used later. For "LaZaRuS" it
     is 7BB. When you go on, you will come here:

     :0000007A 8D4605                  lea eax, dword ptr [esi+05] ;; EAX POINTS TO NAME
     :0000007D 99                      cdq
     :0000007E F7FB                    idiv ebx ;; DIVIDE BY THE LENGTH OF THE NAME
     :00000080 46                      inc esi
     :00000081 49                      dec ecx
     :00000082 83F9F6                  cmp ecx, FFFFFFF6 ;; DID WE LOOP TEN TIMES?
     :00000085 8A043A                  mov al, byte ptr [edx+edi] ;; GET A CHAR FROM THE NAME
     :00000088 88811A894900            mov byte ptr [ecx+0049891A], al ;; SAVE THE CHAR ELSEWHERE
     :0000008E 7FEA                    jg 0000007A ;; IF NOT LOOPED TEN TIMES, THEN JUMP
     
     This loop converts the name into a 10-char-long string. Look at the algo and you will
     find out in which way. I won't explain, as it doesn't matter. We just need the converted
     string. Little later you will find that the name is converted again:

     :00000094 8BC6                    mov eax, esi ;; EAX AND ESI CONTAIN THE "MAGIC VALUE"
     :00000096 8A9910894900            mov bl, byte ptr [ecx+00498910] ;; BL GETS i. CHAR
     :0000009C 99                      cdq
     :0000009D 33C2                    xor eax, edx ;; EAX = EAX XOR EDX
     :0000009F 83C62C                  add esi, 0000002C ;; ESI = ESI + 2C
     :000000A2 2BC2                    sub eax, edx ;; EAX = EAX - EDX
     :000000A4 25FF000000              and eax, 000000FF ;; ONLY AL IS USED FROM EAX
     :000000A9 33C2                    xor eax, edx ;; EAX = EAX XOR EDX
     :000000AB 2BC2                    sub eax, edx ;; EAX = EAX - EDX
     :000000AD 32D8                    xor bl, al ;; BL = BL XOR AL
     :000000AF 889910894900            mov byte ptr [ecx+00498910], bl ;; NEW CHAR
     :000000B5 41                      inc ecx
     :000000B6 83F90A                  cmp ecx, 0000000A ;; TRANSFORMED ALL 10 CHARS?
     :000000B9 7CD9                    jl 00000094 ;; IF NOT, THEN JUMP

     Here the 10-char-string gets once again transformed and overwritten.

     :000000BD 8BC1                    mov eax, ecx
     :000000BF 8A9910894900            mov bl, byte ptr [ecx+00498910] ;; GET CHAR
     :000000C5 99                      cdq
     :000000C6 F77C2414                idiv [esp+14] ;; DIVIDE BY 1C
     :000000CA 8B442418                mov eax, dword ptr [esp+18] ;; SEE BELOW
     :000000CE 8A1402                  mov dl, byte ptr [edx+eax] ;; GET A CHAR FROM STRING
     :000000D1 32DA                    xor bl, dl ;; XOR CHAR OF NAME WITH CHAR OF STRING
     :000000D3 889910894900            mov byte ptr [ecx+00498910], bl ;; CHAR = NEW VALUE
     :000000D9 41                      inc ecx
     :000000DA 83F90A                  cmp ecx, 0000000A ;; TRANSFORMED ALL 10 CHARS?
     :000000DD 7CDE                    jl 000000BD ;; IF NOT, THEN JUMP

     Above, the 10-char-name string gets transformed once again. This time it gets XORed
     with value from the string "-2..4nsw.-dfe./jkslm0qqmndfe" (w/o quotation marks).
     So, finally we reached the end: The next loop transforms the string into a valid
     serial :)
     
     :000000E1 0FBE8110894900          movsx eax, byte ptr [ecx+00498910] ;; EAX = CHAR
     :000000E8 85C0                    test eax, eax ;; EAX = 0?
     :000000EA 7D02                    jge 000000EE ;; JUMP IF EAX > 0
     :000000EC F7D8                    neg eax ;; EAX = EAX * (-1)
     :000000EE 99                      cdq
     :000000EF BE0A000000              mov esi, 0000000A
     :000000F4 F7FE                    idiv esi ;; DIVIDE BY 10 (0Ah)
     :000000F6 80C230                  add dl, 30 ;; CONVERT TO DIGIT BY ADDING 30h
     :000000F9 889110894900            mov byte ptr [ecx+00498910], dl ;; WRITE SERIAL TO MEM
     :000000FF 41                      inc ecx
     :00000100 3BCE                    cmp ecx, esi ;; ESI = 10
     :00000102 7CDD                    jl 000000E1 ;; JUMP IF < 10 DIGITS CREATED

     So, for the very end another transformation:
     :00000104 803D1089490030          cmp byte ptr [00498910], 30
     :0000010B 7507                    jne 00000114
     :0000010D C6051089490033          mov byte ptr [00498910], 33
     :00000114 B810894900              mov eax, 00498910

     If the serial starts with "0", it will be replaced with "3".

     Trace out of the call and you will end in front of the comparison of the
     good and bad serial:

     :00000000 50                      push eax ;; EAX = REAL SERIAL
     :00000001 56                      push esi ;; ESI = ENTERED SERIAL
     :00000002 E8ABF1FFFF              call FFFFF1B2 ;; COMPARE THEM
     :00000007 83C408                  add esp, 00000008 ;; FIX THE STACK
     :0000000A 84C0                    test al, al ;; IS AL = 0
     :0000000C 0F8407010000            je 00000119 ;; IF SO, THEN JUMP
     :00000012 53                      push ebx ;; EBX POINTS TO YOUR NAME
     :00000013 6A20                    push 00000020 ;; 20h = " "
     :00000015 E8C8F4FFFF              call FFFFF4E2 ;; CHECKS IF " " IS IN YOUR NAME
     :0000001A 83C408                  add esp, 00000008
     :0000001D 85C0                    test eax, eax ;; EAX = 0 (NO " ") ?
     :0000001F 0F84F4000000            je 00000119 ;; IF SO, THEN JUMP
     :00000025 8D85BF990000            lea eax, dword ptr [ebp+000099BF] ;; SEE BELOW
     :0000002B 50                      push eax ;; PUSH STRING
     :0000002C 53                      push ebx ;; PUSH NAME
     :0000002D E8D0F4FFFF              call FFFFF502 ;; DOES NAME CONTAIN INVALID CHARS
     :00000032 83C408                  add esp, 00000008
     :00000035 85C0                    test eax, eax ;; IS EAX = 0? (NO INV. CHARS)
     :00000037 0F85DC000000            jne 00000119 ;; IF NOT, THEN JUMP

     So, here we see that (1) our name has to contain a space (00000015) and it
     musn't contain any of the following chars: !.$%&/()=?{[]}#*+ (00000025).

     Later it blacklists the following names: Animalo, Mad Tulip, MasterDeveloperCode,
     Matthes Bender, Markus Wichitill and RedWolf Design.
     btw: Dunno, why they blacklisted "Animalo" and "MasterDeveloperCode" - It wouldn't be
     possible to register with those names as they don't contain a " " ;)

     So, that's all. You will find the source of a keygen (MASM) inside the zip file.

III. BTW

To make sure nobody pisses me off for anything: The process patcher source is 90% from Natzgul
and the skeleton of the keygen was borrowed from rubor.

visit hello.to/lazarus and hellforge.tsx.org
mail: lazarus_hf@hotmail.com

Greetings go to: +Sandman, Acid Burn, alpine, Borna Janes, Carpathia,
CrazyKnight, DEATH, DEZM, dimwitz, DnNuke, duelist, Eternal Bliss, Fravia+,
Iczelion, Jordan, KnowledgeIsPower, Knotty, Lucifer48, MisterE, Neural Noise,
noos, Prof.X, R!SC, rubor, Shadow, SiG, tC, The AntiXryst, The Hobgoblin,
TORN@DO, viny, Volatility, wAj, _y and all the guys I forget and I'll add
next time.